package task2_2.com.at.count;

import task2_2.com.at.count.polsk.Grammar;
import task2_2.com.at.count.polsk.CounterException;
import task2_2.com.at.count.polsk.Lexeme;

import java.math.BigDecimal;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;

public class Function {

    public final static int INTEGER_TYPE    = 1;
    public final static int DOUBLE_TYPE     = 2;

    private String name;
    private boolean standard;
    private int paramCount;
    private List paramNames;
    private String body;
    private int type;

    //private Map paramNames;

    private Map allreadyDefinedFunctions;
    private Grammar grammar;

    private Counter funcCounter;

    protected Function(String name, int paramCount) {
        this.name = name;
        this.paramCount = paramCount;
        this.standard = true;
        this.allreadyDefinedFunctions = null;
        this.funcCounter = null;
        this.paramNames = null;
    }

    protected Function(int type, String prototype, String body, Map allreadyDefinedFunctions, Grammar grammar) throws CounterException {

        this.allreadyDefinedFunctions = allreadyDefinedFunctions;
        this.grammar = grammar;
        this.funcCounter = new Counter(this.allreadyDefinedFunctions, this.grammar);

        int firstBracket, lastBracket;

        firstBracket = prototype.indexOf("(");
        lastBracket  = prototype.lastIndexOf(")");

        if (firstBracket == -1 || lastBracket == -1 || firstBracket > lastBracket) {
            throw new CounterException();
        }

        this.name = checkFunctionName(prototype.substring(0, firstBracket));
        this.paramNames = new ArrayList();
        String params = prototype.substring(firstBracket + 1, lastBracket);
        params = params.trim();
        if (params.length() > 0) {
            int commaIndx = params.indexOf(",");
            String curParam;
            this.paramCount = 0;
            while (commaIndx != -1) {
                curParam = params.substring(0, commaIndx);

                this.paramNames.add(this.funcCounter.addParameter(curParam));
                this.paramCount++;
                params = params.substring(commaIndx + 1);
                commaIndx = params.indexOf(",");
            }
            this.paramNames.add(this.funcCounter.addParameter(params));
            this.paramCount++;
        } else {
            this.paramCount = 0;
        }
        // TODO: make body using polsk
        this.body = body;
        this.type = type;
        this.standard = false;

        this.funcCounter.setExpression(this.body);
        this.funcCounter.generateInvertedPolNotayion();
    }

    private String checkFunctionName(String name) throws CounterException{
        name = name.trim();
        if (name.length() > 0 && name.matches("[a-zA-Z][[a-zA-Z][0-9]]*?")) {
            return name;
        }
        throw new CounterException();
    }

    public boolean isStandard() {
        return standard;
    }

    public String getName() {
        return name;
    }

    public int getParamCount() {
        return paramCount;
    }

    public int getType() {
        return type;
    }

    public BigDecimal count(List lexemes) throws CounterException {

        if (standard) {

            Lexeme lex0 = (Lexeme) lexemes.get(0);

            if (name.equalsIgnoreCase("sin") ) {
                return new BigDecimal(Math.sin(lex0.getBigDecimalValue().doubleValue()));
//                return lex0.getDouleValue() + 10;
            } else if (name.equalsIgnoreCase("cos") ) {
                return new BigDecimal(Math.cos(lex0.getBigDecimalValue().doubleValue()));
//                return lex0.getDouleValue() + 100;
            } else if (name.equalsIgnoreCase("tan") ) {
                return new BigDecimal(Math.tan(lex0.getBigDecimalValue().doubleValue()));
            } else if (name.equalsIgnoreCase("abs") ) {
                return lex0.getBigDecimalValue().abs();
            } else {
                throw new CounterException();
            }
        }

        for (int i=0; i<lexemes.size(); i++) {
            Lexeme lex = (Lexeme) lexemes.get(i);
            funcCounter.setParameter((String)paramNames.get(i), lex.getBigDecimalValue().doubleValue());
        }

        return funcCounter.count();
    }
}
